package com.zj.authorizationserver.config;

import com.zj.authorizationserver.component.JwtAuthenticationTokenFilter;
import com.zj.authorizationserver.component.RestAuthenticationEntryPoint;
import com.zj.authorizationserver.component.RestfulAccessDeniedHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;


/**
 * SpringSecurity的配置
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Resource
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Resource
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Resource
    private UserDetailsService userDetailsService;

    /**
     * 配置安全拦截机制
     * @param httpSecurity
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        // 允许对于网站静态资源的无授权访问
        for (String url : ignoreUrlsConfig.getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        registry.antMatchers("/error").permitAll();
        //跨域请求会先进行一次options请求
        registry.antMatchers(HttpMethod.OPTIONS)
                .permitAll();

        // 关闭跨站请求防护 由于使用的是JWT，我们这里不需要csrf
        registry.and()
                .csrf()
                .disable();
        // 禁用缓存
        registry.and()
                .headers()
                .cacheControl();

        //不使用session
        registry.and().sessionManagement()
                // 基于token，所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);

        // 任何请求需要身份认证
        registry.and()
                .authorizeRequests()
//                .antMatchers("/**").permitAll()//测试时可放开全部允许访问
                .anyRequest()
                .authenticated() // 除上面外的所有请求全部需要鉴权认证
                .and()
                .formLogin()
                .loginPage("/login")//登录页面(前后端不分离)
                .usernameParameter("username").passwordParameter("password") //自定义登录用户名密码属性名,默认为username和password
                .permitAll();


         //添加自定义未授权和未登录结果返回
        registry.and()
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);

        // 添加自定义权限拦截器 JWT过滤器
        registry.and()
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);


    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }


    /**
     * 密码编译器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers("/assets/**")
                .antMatchers("/static.assets/**");
    }

}
